﻿using AutoUpdaterDotNET;
using System;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Media;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace FaceIdentifier
{
    public partial class Main : Form
    {
        private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(Main));
        private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
        private readonly VideoDeviceSource _videoDeviceSource = new VideoDeviceSource();
        private ServiceConfig config = new ServiceConfig();
        private readonly FaceApi _faceApi;
        private const string SaveRecordPath = "识别记录";

        private readonly SoundPlayer _successVoice;
        private readonly SoundPlayer _errorVoice;
        private readonly FaceRepositoryManage _faceRepositoryManage;

        private bool _skipFps;
        private bool isAddImage = false;
        private bool isReload = true;

        private string format = "yyyy年MM月dd日HH时mm分ss秒";
        private string message = "";

        private Size pictureBoxSize = Size.Empty;
        private DateTime skipBeforeTime = DateTime.Now;

        private StringBuilder cardId = new StringBuilder();

        public Main()
        {
            InitializeComponent();

            pictureBoxSize = pictureBox1.Size;

            if (!string.IsNullOrWhiteSpace(config.UpdateUrl()))
            {
                Log.InfoFormat("启动检查更新:{0}", config.UpdateUrl());
                AutoUpdater.Start(config.UpdateUrl());
            }
            else
            {
                Log.Warn("未启用客户版本更新设置");
            }

            try
            {
                serialPort1.Open();
            }
            catch (Exception e)
            {
                Log.Error("打开串口1失败", e);
                MessageBox.Show("打开COM1失败,将无法进行开闸操作");
            }

            _faceApi = new FaceApiImpl();


            _faceRepositoryManage = new FaceRepositoryManage(_faceApi);


            var successFile = Path.Combine("Resources", "success.wav");
            if (File.Exists(successFile))
            {
                _successVoice = new SoundPlayer(successFile);
                Log.InfoFormat("加载success音效文件成功:{0}", successFile);
            }

            var errorFile = Path.Combine("Resources", "error.wav");
            if (File.Exists(errorFile))
            {
                _errorVoice = new SoundPlayer(errorFile);
                Log.InfoFormat("加载error音效文件成功:{0}", errorFile);
            }


            if (!Directory.Exists(SaveRecordPath))
            {
                Directory.CreateDirectory(SaveRecordPath);
                Log.InfoFormat("创建识别记录文件夹:{0}", SaveRecordPath);
            }
            Task.Factory.StartNew(FaceIdentifier, _cancellationTokenSource.Token);
        }

        private void FaceIdentifier()
        {
            Task.Delay(3000).Wait();

            int errorTimes = 0;
            int noPeopleTimes = 0;
            while (!_cancellationTokenSource.IsCancellationRequested)
            {
                try
                {
                    if(skipBeforeTime.Subtract(DateTime.Now).Milliseconds > 0)
                    {
                        continue;
                    }
                    if (isAddImage)
                    {
                        message = "正在添加用户";
                        Thread.Sleep(2000);
                        errorTimes = 0;
                        noPeopleTimes = 0;
                        continue;
                    }

                    if (isReload)
                    {
                        isReload = false;
                        try
                        {
                            message = "正在初始化";
                            _faceApi.Init();
                            _faceApi.ReloadFace();
                            _faceRepositoryManage.StartService();
                            message = "欢迎使用人脸识别认证系统";
                        }
                        catch (Exception ex)
                        {
                            Log.Error("重新加载人脸库失败", ex);
                        }
                        skipBeforeTime = DateTime.Now.AddSeconds(2);
                        errorTimes = 0;
                        noPeopleTimes = 0;
                        continue;
                    }
                    DateTime dt1 = DateTime.Now;
                    using (var bitmap = _videoDeviceSource.GetCurrentFrame())
                    {
                        if (bitmap == null)
                        {
                            continue;
                        }
                        string key = _faceApi.Match(bitmap);
                        Log.DebugFormat("识别耗时:{0}", DateTime.Now.Subtract(dt1).TotalMilliseconds);
                        //没有人脸显示 
                        if (key == null)
                        {
                            Log.Debug("识别结果：没有人脸");
                            noPeopleTimes = noPeopleTimes + 1;
                            errorTimes = 0;

                            if (noPeopleTimes > 5)
                            {
                                message = "欢迎使用人脸识别认证系统";
                            }
                            continue;
                        }

                        //没有识别到用户
                        if (key == "")
                        {
                            Log.Debug("识别结果：没有匹配到人脸");
                            noPeopleTimes = 0;
                            errorTimes = errorTimes + 1;
                            if (errorTimes > 0 && errorTimes % 5 == 0)
                            {
                                message = "认证失败";
                                PlayError();
                            }
                            continue;
                        }
                        //识别到用户
                        if (!string.IsNullOrEmpty(key))
                        {
                            Log.InfoFormat("识别结果：{0}", key);
                            noPeopleTimes = 0;
                            errorTimes = 0;
                            SaveIdentifierRecord(key, bitmap);
                        }

                    }
                }
                catch (Exception ex)
                {
                    Log.Error("识别人脸异常", ex);
                }
                finally
                {
                    Thread.Sleep(500);
                }

            }
        }

        private void SaveIdentifierRecord(string key, Bitmap img)
        {

            using (var bitmap = Zoom(img, img.Width / 4, img.Height / 4))
            {
                try
                {
                    if (string.IsNullOrEmpty(key)) return;

                    PrivilegeInfo privilegeInfo = _faceApi.GetInfo(key);

                    if (privilegeInfo == null)
                    {
                        Log.Error("本地未缓存人脸信息");
                        return;
                    }

                    var show_name = privilegeInfo.personName.Substring(0, 1).PadRight(privilegeInfo.personName.Length, '*');

                    openDoor();
                    PlaySuccess();
                    message = show_name + "认证成功";

                    var fileName = privilegeInfo.personSN + "_" + privilegeInfo.personName + "_" + DateTime.Now.ToString("yyyyMMddHHmmss");
                    var file = Path.Combine(SaveRecordPath, $"{fileName}.jpg");
                    bitmap.Save(file);
                    Log.InfoFormat("保存人脸记录成功", file);
                    skipBeforeTime = DateTime.Now.AddSeconds(2);
                }
                catch (Exception e)
                {
                    Log.Error("保存人脸记录异常", e);
                }
            }
        }

        private Bitmap Zoom(Bitmap bitmap, int new_Width, int new_Height)
        {
            Bitmap newBitmap = new Bitmap(new_Width, new_Width);
            using (Graphics newG = Graphics.FromImage(newBitmap))
            {
                newG.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Low;
                newG.DrawImage(bitmap, new Rectangle(0, 0, new_Width, new_Width), new Rectangle(0, 0, bitmap.Width, bitmap.Height), GraphicsUnit.Pixel);
            }
            return newBitmap;
        }

        private void openDoor()
        {
            if (serialPort1.IsOpen)
            {
                try
                {
                    var hex = config.getOpenDoorCode();
                    if (string.IsNullOrEmpty(hex))
                    {
                        return;
                    }

                    string[] splits = hex.Split(' ');

                    if (splits.Length == 0)
                    {
                        return;
                    }

                    var inputByteArray = new byte[splits.Length];
                    for (var x = 0; x < inputByteArray.Length; x++)
                    {
                        var i = Convert.ToInt32(splits[x], 16);
                        inputByteArray[x] = (byte)i;
                    }
                    serialPort1.Write(inputByteArray, 0, inputByteArray.Length);
                }
                catch (Exception e)
                {
                    Log.Error("开闸失败", e);
                    throw;
                }
            }
        }

        private void Main_Load(object sender, EventArgs e)
        {
            try
            {
                _videoDeviceSource._fps += VideoDeviceSource_NewFrame;
                _videoDeviceSource.Start();
            }
            catch (Exception ex)
            {
                Log.Error("系统启动失败", ex);
            }
        }

        private void VideoDeviceSource_NewFrame(object sender, Bitmap image)
        {
            try
            {
                _skipFps = !_skipFps;
                if (_skipFps)
                {
                    return;
                }

                if (isAddImage)
                {
                    return;
                }
                pictureBox1.Invalidate();
            }
            catch (Exception e)
            {
                Log.Error("采集相机异常", e);
            }
        }

        private void Main_FormClosed(object sender, FormClosedEventArgs e)
        {
            Log.Info("系统退出");
            _cancellationTokenSource.Cancel();
            _videoDeviceSource.Stop();
            if (_faceRepositoryManage != null)
            {
                _faceRepositoryManage.StopService();
            }

            if (_successVoice != null)
            {
                _successVoice.Dispose();
            }
            if (_errorVoice != null)
            {
                _errorVoice.Dispose();
            }
        }

        public void PlaySuccess()
        {
            if (_successVoice != null)
            {
                try
                {
                    _successVoice.Play();
                }
                catch (Exception e)
                {
                    Log.Error("播放认证成功音效异常", e);
                }
            }
        }


        public void PlayError()
        {
            if (_errorVoice != null)
            {
                try
                {
                    _errorVoice.Play();
                }
                catch (Exception e)
                {
                    Log.Error("播放认证失败音效异常", e);
                }
            }
        }

        private void UpdateTime_Tick(object sender, EventArgs e)
        {
            try
            {
                var now = DateTime.Now;
                label3.Text = now.ToString(format, DateTimeFormatInfo.InvariantInfo) + "(" + (_faceApi == null ? 0 : _faceApi.FaceCount()) + ")";
                label1.Text = message;
            }
            catch (Exception ex)
            {

                Log.Error("更新提示异常", ex);
            }
        }

        private void PictureBox1_Paint(object sender, PaintEventArgs e)
        {
            _videoDeviceSource.PaintToPictureBox(pictureBox1, e.Graphics);
        }

        private void toolStripMenuItem1_Click(object sender, EventArgs e)
        {
            try
            {
                isAddImage = true;
                AddFace addFace = new AddFace();
                addFace.Init(_videoDeviceSource.GetCurrentCropFrame(), _faceApi);
                addFace.ShowDialog();
            }
            finally
            {
                isAddImage = false;
            }
        }

        private void 退出ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void 重新加载人脸库ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var files = Directory.GetFiles("人脸库").Where(w => w.EndsWith("data"));
            foreach(var file in files)
            {
                File.Delete(file);
            }
            isReload = true;
        }

        private void Main_KeyUp(object sender, KeyEventArgs e)
        {
            //Log.InfoFormat("key up {0}", e.KeyCode);
        }

        private void Main_KeyPress(object sender, KeyPressEventArgs e)
        {
            Log.InfoFormat("key up {0}", e.KeyChar);
            if(e.KeyChar == '\x0d')
            {
                skipBeforeTime = DateTime.Now.AddSeconds(2);
                new Thread(new ThreadStart(validCard)).Start();
                
            }else
            {
                cardId.Append(e.KeyChar);
            }
        }

        private void validCard()
        {
            try
            {
                string card = cardId.ToString().Trim().ToUpper();
                Log.InfoFormat("卡片内码:{0}", card);
                cardId.Clear();
                if(card.Length == 8)
                {
                    string open = _faceRepositoryManage.HttpValidCard(card);
                    if (!string.IsNullOrEmpty(open))
                    {
                        message = open + "验证成功";
                        PlaySuccess();
                        openDoor();
                    }
                    else
                    {
                        message = "验证失败";
                        PlayError();
                    }
                }else
                {
                    string open = _faceRepositoryManage.HttpValidCode(card);
                    if (!string.IsNullOrEmpty(open))
                    {
                        message = open + "验证成功";
                        PlaySuccess();
                        openDoor();
                    }
                    else
                    {
                        message = "验证失败";
                        PlayError();
                    }
                }
                
            }
            catch (Exception ex)
            {
                Log.Error("验证卡片或二维码异常",ex);
            }
        }
    }
}
